{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 部署活动和过滤器 <a class=\"anchor\" id=\"top\"></a>\n",
    "\n",
    "在这个notebook中，您将在Amazon Personalize中部署活动并与之互动。\n",
    "\n",
    "1. [简介](#intro)\n",
    "1. [创建活动](#create)\n",
    "1. [与活动互动](#interact)\n",
    "1. [批量推荐](#batch)\n",
    "1. [Wrap up](#wrapup)\n",
    "\n",
    "## Introduction <a class=\"anchor\" id=\"intro\"></a>\n",
    "[回到顶部](#top)\n",
    "\n",
    "此时，您应该具有多个解决方案，并且每个解决方案至少具有一个解决方案版本。一旦创建了解决方案版本，就可以从它们那里获得推荐，并对他们的整体行为有所了解。\n",
    "\n",
    "该笔记本首先将先前笔记本中的每个解决方案版本部署到单独的活动中。 一旦它们处于活动状态，就有资源可供查询推荐，并且可以使用辅助函数将输出摘要为更易于理解的内容。\n",
    "\n",
    "与您的客户一起使用Amazon Personalize时，您可以修改辅助程序功能，以便适合其数据输入文件的结构，以保持其他呈现工作工作。\n",
    "\n",
    "首先，我们需要再次导入库，从以前的notebook中加载值，并加载SDK。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "from time import sleep\n",
    "import json\n",
    "from datetime import datetime\n",
    "import uuid\n",
    "import random\n",
    "\n",
    "import boto3\n",
    "import botocore\n",
    "from botocore.exceptions import ClientError\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store -r"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "personalize = boto3.client('personalize')\n",
    "personalize_runtime = boto3.client('personalize-runtime')\n",
    "\n",
    "# 建立一个到Personalize'的事件流\n",
    "personalize_events = boto3.client(service_name='personalize-events')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建活动 <a class=\"anchor\" id=\"create\"></a>\n",
    "[回到顶部](#top)\n",
    "\n",
    "活动是一个托管的解决方案版本；一个您可以查询建议的终端节点。在部署一个活动时，您可以设置最低每秒吞吐量（TPS）值。 与亚马逊云科技中的许多服务一样，该服务将根据需求自动扩展，但是如果延迟至关重要，则您可能需要提前预置以满足更大的需求。 对于此POC和演示，所有最小吞吐量阈值均设置为1。更多信息请查看[价格页面](https://aws.amazon.com/personalize/pricing/)\n",
    "\n",
    "让我们开始部署这个活动。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 用户个性化\n",
    "\n",
    "为您的用户个性化解决方案版本部署活动。 部署一个活动大约需要10分钟的时间。 通常，我们将使用while循环进行轮询，直到任务完成为止。 不过这个while任务将阻止notebook其他单元格代码的执行，此处我们的目标是创建多个活动。因此，我们将在notbook中为所有活动设置while循环。 此外，您还将找到有关在亚马逊云科技控制台中查看进度的说明。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "userpersonalization_create_campaign_response = personalize.create_campaign(\n",
    "    name = \"personalize-poc-userpersonalization\",\n",
    "    solutionVersionArn = userpersonalization_solution_version_arn,\n",
    "    minProvisionedTPS = 1\n",
    ")\n",
    "\n",
    "userpersonalization_campaign_arn = userpersonalization_create_campaign_response['campaignArn']\n",
    "print(json.dumps(userpersonalization_create_campaign_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### SIMS\n",
    "\n",
    "为您的SIMS解决方案版本部署活动。 部署一个活动大约需要10分钟的时间。 通常，我们将使用while循环进行轮询，直到任务完成为止。 不过这个while任务将阻止notebook其他单元格代码的执行，此处我们的目标是创建多个活动。因此，我们将在notbook中为所有活动设置while循环。 此外，您还将找到有关在亚马逊云科技控制台中查看进度的说明。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sims_create_campaign_response = personalize.create_campaign(\n",
    "    name = \"personalize-poc-SIMS\",\n",
    "    solutionVersionArn = sims_solution_version_arn,\n",
    "    minProvisionedTPS = 1\n",
    ")\n",
    "\n",
    "sims_campaign_arn = sims_create_campaign_response['campaignArn']\n",
    "print(json.dumps(sims_create_campaign_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 个性化排名\n",
    "\n",
    "为您的个性化排名解决方案版本部署活动。 部署一个活动大约需要10分钟的时间。 通常，我们将使用while循环进行轮询，直到任务完成为止。 不过这个while任务将阻止notebook其他单元格代码的执行，此处我们的目标是创建多个活动。因此，我们将在notbook中为所有活动设置while循环。 此外，您还将找到有关在亚马逊云科技控制台中查看进度的说明。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rerank_create_campaign_response = personalize.create_campaign(\n",
    "    name = \"personalize-poc-rerank\",\n",
    "    solutionVersionArn = rerank_solution_version_arn,\n",
    "    minProvisionedTPS = 1\n",
    ")\n",
    "\n",
    "rerank_campaign_arn = rerank_create_campaign_response['campaignArn']\n",
    "print(json.dumps(rerank_create_campaign_response, indent=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 查看活动创建状态\n",
    "\n",
    "在我们前面所提到的，如何在控制台中查看状态更新:\n",
    "\n",
    "* 在另一个浏览器选项卡中，打开亚马逊云科技控制台。\n",
    "* 切换到该标签并在顶部搜索`Personalize`服务，然后转到该服务页面。\n",
    "* 点击 `查看数据集组`.\n",
    "* 点击数据集组的名称，一般来说是名称中带有“POC”字样的那个。\n",
    "* 点击 `活动`.\n",
    "* 现在您将看到上面创建的所有活动的列表，包括带有活动状态的列。 一旦它的状态为`Active`，你的活动就已经准备好接受查询。\n",
    "\n",
    "或者，只需运行下面的单元格中的代码即可跟踪活动的创建状态。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "in_progress_campaigns = [\n",
    "    userpersonalization_campaign_arn,\n",
    "    sims_campaign_arn,\n",
    "    rerank_campaign_arn\n",
    "]\n",
    "\n",
    "max_time = time.time() + 3*60*60 # 3 hours\n",
    "while time.time() < max_time:\n",
    "    for campaign_arn in in_progress_campaigns:\n",
    "        version_response = personalize.describe_campaign(\n",
    "            campaignArn = campaign_arn\n",
    "        )\n",
    "        status = version_response[\"campaign\"][\"status\"]\n",
    "        \n",
    "        if status == \"ACTIVE\":\n",
    "            print(\"Build succeeded for {}\".format(campaign_arn))\n",
    "            in_progress_campaigns.remove(campaign_arn)\n",
    "        elif status == \"CREATE FAILED\":\n",
    "            print(\"Build failed for {}\".format(campaign_arn))\n",
    "            in_progress_campaigns.remove(campaign_arn)\n",
    "    \n",
    "    if len(in_progress_campaigns) <= 0:\n",
    "        break\n",
    "    else:\n",
    "        print(\"At least one campaign build is still in progress\")\n",
    "        \n",
    "    time.sleep(60)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建过滤器 <a class=\"anchor\" id=\"interact\"></a>\n",
    "[回到顶部](#top)\n",
    "\n",
    "现在，所有活动均已部署并处于活动状态，我们可以创建过滤器。 可以为“项目”和“事件”创建过滤器。 视频点播中的过滤器的一些常见用例是：\n",
    "\n",
    "基于项目元数据的分类过滤器：通常，您的项目元数据会包含有关您的标题的信息，例如流派，关键字，年份，年代等。对此进行过滤可以在该数据中提供建议，例如动作片。\n",
    "\n",
    "事件：您可能希望过滤掉某些事件并根据这些事件提供结果，例如将标题从“要观看的建议”建议移到“再次观看”建议。\n",
    "\n",
    "让我们看一下项目元数据和用户交互，这样我们就可以知道可以创建哪种类型的过滤器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 通过读取正确的源CSV为物品创建dataframe\n",
    "items_df = pd.read_csv(data_dir + '/item-meta.csv', sep=',', index_col=0)\n",
    "#interactions_df = pd.read_csv(data_dir + '/interactions.csv', sep=',', index_col=0)\n",
    "\n",
    "# 查看一些示例数据\n",
    "items_df.head(10)\n",
    "#interactions_df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们要做的是确定要筛选的类型，为此，我们需要所有类型的列表。 首先，我们将获取GENRE列的所有唯一值，然后在`|`上拆分字符串（如果存在的话），然后将每一个添加到一个长列表中，该列表将转换为一组以提高效率。 然后将该集合放入一个列表中，以便可以对其进行迭代，然后我们可以使用创建过滤器的API。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "unique_genre_field_values = items_df['GENRE'].unique()\n",
    "\n",
    "genre_val_list = []\n",
    "\n",
    "def process_for_bar_char(val, val_list):\n",
    "    if '|' in val:\n",
    "        values = val.split('|')\n",
    "        for item in values:\n",
    "            val_list.append(item)\n",
    "    elif '(' in val:\n",
    "        pass\n",
    "    else:\n",
    "        val_list.append(val)\n",
    "    return val_list\n",
    "    \n",
    "\n",
    "for val in unique_genre_field_values:\n",
    "    genre_val_list = process_for_bar_char(val, genre_val_list)\n",
    "\n",
    "genres_to_filter = list(set(genre_val_list))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "genres_to_filter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，我们有了数据集中存在的所有类型。 目前，Personalize的一个软限制是总共10个过滤器，如果我们拥有更多类型，我们将随机选择7个，以便稍后为2个基于交互的过滤器留出空间，并为基于年份的推荐留出额外的过滤器。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "genres_to_filter = random.sample(genres_to_filter, 7)\n",
    "genres_to_filter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在为元数据类型过滤器创建一个列表，然后使用下面单元格中的代码创建实际的过滤器。 请注意，这将需要几分钟才能完成。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建过滤器列表:\n",
    "meta_filter_arns = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 遍历类型\n",
    "for genre in genres_to_filter:\n",
    "    # 首先创建一个过滤器\n",
    "    try:\n",
    "        createfilter_response = personalize.create_filter(\n",
    "            name=genre,\n",
    "            datasetGroupArn=dataset_group_arn,\n",
    "            filterExpression='INCLUDE ItemID WHERE Items.GENRE IN (\"'+ genre +'\")'\n",
    "        )\n",
    "        # 将ARN添加到列表\n",
    "        meta_filter_arns.append(createfilter_response['filterArn'])\n",
    "        print(\"Creating: \" + createfilter_response['filterArn'])\n",
    "    \n",
    "    # 如果失败，稍等一会儿\n",
    "    except ClientError as error:\n",
    "        # 这里我们只关心是否出现的是出现限流的问题\n",
    "        if error.response['Error']['Code'] != 'LimitExceededException':\n",
    "            print(error)\n",
    "        else:    \n",
    "            time.sleep(120)\n",
    "            createfilter_response = personalize.create_filter(\n",
    "                name=genre,\n",
    "                datasetGroupArn=dataset_group_arn,\n",
    "                filterExpression='INCLUDE ItemID WHERE Items.GENRE IN (\"'+ genre +'\")'\n",
    "            )\n",
    "            # 将ARN添加到列表\n",
    "            meta_filter_arns.append(createfilter_response['filterArn'])\n",
    "            print(\"Creating: \" + createfilter_response['filterArn'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "让我们还为“已观看”和“未观看”的内容创建2个事件过滤器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 通过读取正确的源CSV创建用于交互的dataframe\n",
    "interactions_df = pd.read_csv(data_dir + '/interactions.csv', sep=',', index_col=0)\n",
    "\n",
    "# 查看一些示例数据\n",
    "interactions_df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "让我们还为“已观看”和“未观看”的内容创建2个事件过滤器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "createwatchedfilter_response = personalize.create_filter(name='watched',\n",
    "    datasetGroupArn=dataset_group_arn,\n",
    "    filterExpression='INCLUDE ItemID WHERE Interactions.event_type IN (\"watch\")'\n",
    "    )\n",
    "\n",
    "createunwatchedfilter_response = personalize.create_filter(name='unwatched',\n",
    "    datasetGroupArn=dataset_group_arn,\n",
    "    filterExpression='EXCLUDE ItemID WHERE Interactions.event_type IN (\"watch\")'\n",
    "    )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，由于我们在项目元数据中提供了年份数据，因此创建一个十年过滤器以仅推荐给定年代中发行的电影，为此workshop，我们将选择1970年代的影片。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "createdecadefilter_response = personalize.create_filter(name='1970s',\n",
    "    datasetGroupArn=dataset_group_arn,\n",
    "    filterExpression='INCLUDE ItemID WHERE Items.YEAR >= 1970 AND Items.YEAR < 1980'\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在完成之前，我们将希望将这些过滤器也添加到列表中，以便以后使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "interaction_filter_arns = [createwatchedfilter_response['filterArn'], createunwatchedfilter_response['filterArn']]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "decade_filter_arns = [createdecadefilter_response['filterArn']]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store sims_campaign_arn\n",
    "%store userpersonalization_campaign_arn\n",
    "%store rerank_campaign_arn\n",
    "%store meta_filter_arns\n",
    "%store interaction_filter_arns\n",
    "%store decade_filter_arns\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "您已经准备好进入最后一个探索性笔记本：'05_Interacting_with_Campaigns_and_Filters.ipynb'。 从浏览器中打开它，您就可以开始与活动进行互动并获取推荐了！"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_python3",
   "language": "python",
   "name": "conda_python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
